/*
 * Ingenic libsysutils Audio encode and decode implement.
 *
 * Copyright (C) 2016 Ingenic Semiconductor Co.,Ltd
 * Author: Lionel <shunxi.zhang@ingenic.com>
 */

#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <stdint.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <fcntl.h>
#include <sys/time.h>

#include <imp/imp_audio.h>
#include <imp/imp_log.h>

#include "codecs/g711-code/g711codec.h"
#include "codecs/adpcm-code/adpcm.h"
#include "codecs/g726-code/g726codec.h"
#include "audio_common.h"
#include "abuf.h"
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define  TAG "adec"

int _adec_g711a2pcm(char *InAudioData, char *OutAudioData, int DataLen)
{
	int Retade = 0;

	if((NULL == InAudioData) && (NULL == OutAudioData) && (0 == DataLen)) {
		IMP_LOG_ERR(TAG, "Error, empty data or transmit failed, exit !\n");
		return -1;
	}

	Retade = g711a_decode((short*)OutAudioData, (unsigned char *)InAudioData, DataLen);

	return Retade;
}

int _adec_g711u2pcm(char *InAudioData, char *OutAudioData, int DataLen)
{
	int Retude = 0;

	if((NULL == InAudioData) && (NULL == OutAudioData) && (0 == DataLen)) {
		IMP_LOG_ERR(TAG, "Error, empty data or transmit failed, exit !\n");
		return -1;
	}

	Retude = g711u_decode((short*)OutAudioData, (unsigned char *)InAudioData, DataLen);

	return Retude;
}

int _adec_adpcm2pcm(char *InAudioData,char *OutAudioData,int DataLen)
{
	if ((NULL == InAudioData && (NULL == OutAudioData) && (0 == DataLen))) {
	   IMP_LOG_ERR(TAG,"Error,empty data or transmit failed,exit!\n");
	   return -1;
   }
	static adpcm_state state;
	adpcm_decoder((char*)InAudioData,(short *)OutAudioData,DataLen*2,&state);

	return 4*DataLen;
}

#define G726_BIT_RATE 4
int _adec_g726_2pcm(char *InAudioData,char *OutAudioData,int DataLen)
{
	int Retude = 0;
	if ((NULL == InAudioData) && (NULL == OutAudioData) && (0 == DataLen)) {
		IMP_LOG_ERR(TAG,"Error,empty data or transmit failed,exit!\n");
		return -1;
	}
	static g726_state_t g726_state;
	static int tmpCount = 0;
	if (0 == tmpCount) {
		g726_init(&g726_state,8000*G726_BIT_RATE);
		tmpCount ++;
	}
	Retude = g726_decode(&g726_state,(short*)OutAudioData,(unsigned char *)InAudioData,DataLen);
	return 2*Retude;
}

/* Audio Decoder API */
#define MAX_AUDIO_DENC_CHN 5
#define MAX_AUDIO_DENC_DECODER 5

typedef struct {
	int chnID;
	int chnEnable;
	pthread_t chnProcess;
	pthread_mutex_t chnLock;
	IMPAudioDecChnAttr chnAttr;
	void *adec_buf;
	int adec_buf_size;
} AudioDencChn;

typedef struct {
	int decoderRegister;
	IMPAudioDecDecoder decoder;
} AudioDencMethod;

static AudioDencChn audioDencChn[MAX_AUDIO_DENC_CHN];
static AudioDencMethod audioDencMethod[PT_MAX + MAX_AUDIO_DENC_DECODER];

static int _g711a_open_decoder(void *decoderAttr, void	*decoder)
{
	return 0;
}

static int _g711a_decode_frm(void *decoder, unsigned char *inbuf,int inLen, unsigned short *outbuf,int *outLen,int *chns)
{
	*outLen = _adec_g711a2pcm((char *)inbuf, (char *)outbuf, inLen);
	return 0;
}

static int _g711a_get_frm_info(void *decoder, void *info)
{
	return 0;
}

static int _g711a_close_decoder(void *decoder)
{
	return 0;
}

static int _g711u_open_decoder(void *decoderAttr, void	*decoder)
{
	return 0;
}

static int _g711u_decode_frm(void *decoder, unsigned char *inbuf,int inLen, unsigned short *outbuf,int *outLen,int *chns)
{
	*outLen = _adec_g711u2pcm((char *)inbuf, (char *)outbuf, inLen);
	return 0;
}

static int _g711u_get_frm_info(void *decoder, void *info)
{
	return 0;
}

static int _g711u_close_decoder(void *decoder)
{
	return 0;
}

static int _g726_open_decoder(void *decoderAttr, void *decoder)
{
	return 0;
}

static int _g726_decode_frm(void *decoder,unsigned char *inbuf,int inLen,unsigned short *outbuf,int *outLen,int *chns)
{
	if((NULL == inbuf) && (NULL == outbuf) && (0 == inLen)){
		IMP_LOG_ERR(TAG,"Error,empty data or transmit failed,exit !\n");
		return -1;
   }
	*outLen = _adec_g726_2pcm((char *)inbuf,(char *)outbuf,inLen);
	return 0;
}

static int _g726_get_frm_info(void *decoder, void *info)
{
	return 0;
}

static int _g726_close_decoder(void *decoder)
{
	return 0;
}

static int _adpcm_open_decoder(void *decoderAttr,void *decoder)
{
	return 0;
}

static int _adpcm_decode_frm(void *decoder,unsigned char *inbuf,int inLen,unsigned short *outbuf,int *outLen,int *chns)
{
	if((NULL == inbuf)||(NULL == outbuf) ||(NULL == outLen)){
		IMP_LOG_ERR(TAG,"empty data,adpcm decode failed\n");
		return -1;
	}
	*outLen = _adec_adpcm2pcm((char *)inbuf,(char *)outbuf,inLen);
	 return 0;
}

static int _adpcm_get_frm_info(void *decoder,void *info)
{
	return 0;
}

static int _adpcm_close_decoder(void *decoder)
{
    return 0;
}

static inline void _adec_chn_lock(pthread_mutex_t *mutex)
{
	pthread_mutex_lock(mutex);
}

static inline void _adec_chn_unlock(pthread_mutex_t *mutex)
{
	pthread_mutex_unlock(mutex);
}

struct adec_buf_data {
	void *node;
	int size;
	int data[0];
};

int IMP_ADEC_CreateChn(int AdChn, IMPAudioDecChnAttr *attr)
{
	int size = 0;
	if(AdChn > MAX_AUDIO_DENC_CHN) {
		IMP_LOG_ERR(TAG, "Invalid Audio Denc Channel.\n");
		return -1;
	}

	if(audioDencChn[AdChn].chnEnable == 1) {
		IMP_LOG_ERR(TAG, "AeChn is already enabled.\n");
		return -1;
	}

	audioDencChn[AdChn].chnID = AdChn;
	audioDencChn[AdChn].chnAttr.type = attr->type;
	audioDencChn[AdChn].chnAttr.bufSize = attr->bufSize;
	audioDencChn[AdChn].chnAttr.value = attr->value;

	size = _ao_get_buf_size();
	if (0 >= size) {
		IMP_LOG_ERR(TAG, "ao_buf size error %d\n", size);
		size = /* numPerFrm */400*sizeof(short);
	}
	pthread_mutex_init(&audioDencChn[AdChn].chnLock, NULL);
	_adec_chn_lock(&audioDencChn[AdChn].chnLock);
	if(audioDencChn[AdChn].adec_buf == NULL) {
		audioDencChn[AdChn].adec_buf =  audio_buf_alloc(audioDencChn[AdChn].chnAttr.bufSize, size+sizeof(struct adec_buf_data), 0);
		audioDencChn[AdChn].adec_buf_size = size;
		if(audioDencChn[AdChn].adec_buf == NULL) {
			IMP_LOG_ERR(TAG, "adec_buf alloc error\n");
			_adec_chn_unlock(&audioDencChn[AdChn].chnLock);
			return -1;
		}
	}
	_adec_chn_unlock(&audioDencChn[AdChn].chnLock);

	/* PT_G711A */
	sprintf(audioDencMethod[PT_G711A].decoder.name, "%s", "PT_G711A");
	audioDencMethod[PT_G711A].decoder.type = PT_G711A;
	audioDencMethod[PT_G711A].decoder.openDecoder = _g711a_open_decoder;
	audioDencMethod[PT_G711A].decoder.decodeFrm = _g711a_decode_frm;
	audioDencMethod[PT_G711A].decoder.getFrmInfo = _g711a_get_frm_info;
	audioDencMethod[PT_G711A].decoder.closeDecoder = _g711a_close_decoder;
	audioDencMethod[PT_G711A].decoderRegister = 1;

	/* PT_G711U */
	sprintf(audioDencMethod[PT_G711U].decoder.name, "%s", "PT_G711U");
	audioDencMethod[PT_G711U].decoder.type = PT_G711U;
	audioDencMethod[PT_G711U].decoder.openDecoder = _g711u_open_decoder;
	audioDencMethod[PT_G711U].decoder.decodeFrm = _g711u_decode_frm;
	audioDencMethod[PT_G711U].decoder.getFrmInfo = _g711u_get_frm_info;
	audioDencMethod[PT_G711U].decoder.closeDecoder = _g711u_close_decoder;
	audioDencMethod[PT_G711U].decoderRegister = 1;

	/* PT_G726 */
	sprintf(audioDencMethod[PT_G726].decoder.name, "%s", "PT_G726");
	audioDencMethod[PT_G726].decoder.type = PT_G726;
	audioDencMethod[PT_G726].decoder.openDecoder = _g726_open_decoder;
	audioDencMethod[PT_G726].decoder.decodeFrm = _g726_decode_frm;
	audioDencMethod[PT_G726].decoder.getFrmInfo = _g726_get_frm_info;
	audioDencMethod[PT_G726].decoder.closeDecoder = _g726_close_decoder;
	audioDencMethod[PT_G726].decoderRegister = 1;

	/* PT_ADPCM */
	sprintf(audioDencMethod[PT_ADPCM].decoder.name, "%s", "PT_ADPCM");
	audioDencMethod[PT_ADPCM].decoder.type = PT_ADPCM;
	audioDencMethod[PT_ADPCM].decoder.openDecoder = _adpcm_open_decoder;
	audioDencMethod[PT_ADPCM].decoder.decodeFrm = _adpcm_decode_frm;
	audioDencMethod[PT_ADPCM].decoder.getFrmInfo = _adpcm_get_frm_info;
	audioDencMethod[PT_ADPCM].decoder.closeDecoder = _adpcm_close_decoder;
	audioDencMethod[PT_ADPCM].decoderRegister = 1;

	if(!audioDencMethod[audioDencChn[AdChn].chnAttr.type].decoderRegister) {
		IMP_LOG_ERR(TAG, "encoder [%d] is not register\n", audioDencChn[AdChn].chnAttr.type);
		return -1;
	}

	if(audioDencMethod[audioDencChn[AdChn].chnAttr.type].decoder.openDecoder)
		audioDencMethod[audioDencChn[AdChn].chnAttr.type].decoder.openDecoder(NULL, NULL);

	audioDencChn[AdChn].chnEnable = 1;
	return 0;
}

int IMP_ADEC_DestroyChn(int AdChn)
{
	int ret = -1;
	if(AdChn > MAX_AUDIO_DENC_CHN) {
		IMP_LOG_ERR(TAG, "Invalid Audio Enc Channel.\n");
		return -1;
	}
	_adec_chn_lock(&audioDencChn[AdChn].chnLock);
	audioDencChn[AdChn].chnEnable = 0;
	if(audioDencChn[AdChn].adec_buf != NULL) {
		audio_buf_free(audioDencChn[AdChn].adec_buf);
		audioDencChn[AdChn].adec_buf = NULL;
	}
	_adec_chn_unlock(&audioDencChn[AdChn].chnLock);
	ret = pthread_mutex_destroy(&audioDencChn[AdChn].chnLock);
	if (0 != ret) {
		IMP_LOG_ERR(TAG, "err: destroy adec chn lock\n");
		return ret;
	}
	return 0;
}

int IMP_ADEC_SendStream(int AdChn, IMPAudioStream *stream, IMPBlock block)
{
	void *buf_node = NULL;
	struct adec_buf_data *data = NULL;
	if(AdChn > MAX_AUDIO_DENC_CHN) {
		IMP_LOG_ERR(TAG, "Invalid Audio Enc Channel.\n");
		return -1;
	}

	if(audioDencChn[AdChn].chnEnable != 1) {
		IMP_LOG_ERR(TAG, "AdChn is not enabled.\n");
		return -1;
	}

	while(1) {
		_adec_chn_lock(&audioDencChn[AdChn].chnLock);
		buf_node = audio_buf_get_node(audioDencChn[AdChn].adec_buf, AUDIO_BUF_EMPTY);
		_adec_chn_unlock(&audioDencChn[AdChn].chnLock);
		if(buf_node) {
			data = audio_buf_node_get_data(buf_node);
			data->size = audioDencChn[AdChn].adec_buf_size;
			if(audioDencMethod[audioDencChn[AdChn].chnAttr.type].decoder.decodeFrm) {
				audioDencMethod[audioDencChn[AdChn].chnAttr.type].decoder.decodeFrm(NULL,
						stream->stream, stream->len, (unsigned short *)data->data, &data->size, NULL);
			}
			data->node = buf_node;
			_adec_chn_lock(&audioDencChn[AdChn].chnLock);
			audio_buf_put_node(audioDencChn[AdChn].adec_buf, buf_node, AUDIO_BUF_FULL);
			_adec_chn_unlock(&audioDencChn[AdChn].chnLock);
			break;
		}
		usleep(SLEEP_TIME);
	}
	return 0;
}

int IMP_ADEC_PollingStream(int AdChn, unsigned int timeout_ms)
{
	unsigned int time_delay = 0;
	void *buf_node = NULL;
	if(AdChn > MAX_AUDIO_DENC_CHN) {
		IMP_LOG_ERR(TAG, "Invalid Audio Enc Channel.\n");
		return -1;
	}

	if(audioDencChn[AdChn].chnEnable != 1) {
		IMP_LOG_ERR(TAG, "AdChn is not enabled.\n");
		return -1;
	}

	do {
		_adec_chn_lock(&audioDencChn[AdChn].chnLock);
		buf_node = audio_buf_try_get_node(audioDencChn[AdChn].adec_buf, AUDIO_BUF_FULL);
		_adec_chn_unlock(&audioDencChn[AdChn].chnLock);
		if (buf_node != NULL)
			break;
		if (buf_node == NULL && time_delay >= timeout_ms) {
			IMP_LOG_ERR(TAG, "%s %d Audio DEC polling timeout.\n", __func__, __LINE__);
			return -1;
		}
		usleep(20 * 1000);
		time_delay += 20;
	}while(1);

	return 0;
}

int IMP_ADEC_GetStream(int AdChn, IMPAudioStream *stream ,IMPBlock block)
{
	void *buf_node = NULL;
	struct adec_buf_data *data = NULL;
	if (AdChn > MAX_AUDIO_DENC_CHN) {
		IMP_LOG_ERR(TAG, "Invalid Audio Enc Channel.\n");
		return -1;
	}
	if (audioDencChn[AdChn].chnEnable != 1) {
		IMP_LOG_ERR(TAG, "AdChn is not enabled.\n");
		return -1;
	}
	do {
		_adec_chn_lock(&audioDencChn[AdChn].chnLock);
		buf_node = audio_buf_get_node(audioDencChn[AdChn].adec_buf, AUDIO_BUF_FULL);
		_adec_chn_unlock(&audioDencChn[AdChn].chnLock);
		if (buf_node) {
			data = audio_buf_node_get_data(buf_node);
			stream->stream = (void *)data->data;
			stream->len = data->size;
			break;
		}
		usleep(SLEEP_TIME);
	} while (block == BLOCK);

	return 0;
}

int IMP_ADEC_ReleaseStream(int AdChn,IMPAudioStream *stream)
{
	void *buf_node = NULL;
	struct adec_buf_data *buf_data = NULL;
	if (AdChn > MAX_AUDIO_DENC_CHN) {
		IMP_LOG_ERR(TAG, "Invalid Audio Enc Channel.\n");
		return -1;
	}
	if (audioDencChn[AdChn].chnEnable != 1) {
		IMP_LOG_ERR(TAG, "AdChn is not enabled.\n");
		return -1;
	}

	buf_data = container_of((void *)stream->stream, struct adec_buf_data, data);
	_adec_chn_lock(&audioDencChn[AdChn].chnLock);
	buf_node = buf_data->node;
	audio_buf_put_node(audioDencChn[AdChn].adec_buf, buf_node, AUDIO_BUF_EMPTY);
	_adec_chn_unlock(&audioDencChn[AdChn].chnLock);
	return 0;
}

int IMP_ADEC_ClearChnBuf(int AdChn)
{
	if (AdChn > MAX_AUDIO_DENC_CHN) {
		IMP_LOG_ERR(TAG, "Invalid Audio Enc Channel.\n");
		return -1;
	}
	if (audioDencChn[AdChn].chnEnable != 1) {
		IMP_LOG_ERR(TAG, "AdChn is not enabled.\n");
		return -1;
	}
	_adec_chn_lock(&audioDencChn[AdChn].chnLock);
	audio_buf_clear(audioDencChn[AdChn].adec_buf);
	_adec_chn_unlock(&audioDencChn[AdChn].chnLock);
	return 0;
}

int IMP_ADEC_RegisterDecoder(int *handle, IMPAudioDecDecoder *decoder)
{
	int handle_tmp = PT_MAX;
	for (handle_tmp = PT_MAX; handle_tmp < (PT_MAX + MAX_AUDIO_DENC_DECODER); handle_tmp++) {
		if(audioDencMethod[handle_tmp].decoderRegister == 0)
			break;
	}

	if(handle_tmp == (PT_MAX + MAX_AUDIO_DENC_DECODER)) {
		IMP_LOG_ERR(TAG, "You have reached the maximum amount of registration.\n");
		return -1;
	}

	memcpy(&audioDencMethod[handle_tmp].decoder, decoder, sizeof(IMPAudioDecDecoder));
	audioDencMethod[handle_tmp].decoderRegister = 1;
	*handle = handle_tmp;

	return 0;
}

int IMP_ADEC_UnRegisterDecoder(int *handle)
{
	if (*handle < PT_MAX || *handle > (PT_MAX + MAX_AUDIO_DENC_DECODER)) {
		IMP_LOG_ERR(TAG, "Invalid handle\n");
		return -1;
	}

	audioDencMethod[*handle].decoderRegister = 0;

	return 0;
}

#ifdef TEST_ADEC
int main()
{
	return 0;
}
#endif
